//
//  DownLoader.m
//  DownloadManager
//
//  Created by 马滕亚 on 2020/4/14.
//  Copyright © 2020 M.Y.A. All rights reserved.
//

#import "DownLoader.h"

#define keyTimeOut 15.0

@interface DownLoader ()<NSURLConnectionDataDelegate>
/** 文件总大小 */
@property (nonatomic, assign) long long expectedContentLength;
/** 文件建议保存路径 */
@property (nonatomic, strong) NSString *filePath;
/** 本地文件总大小 */
@property (nonatomic, assign) long long currentSize;
/** url */
@property (nonatomic, strong) NSURL *downloadUrl;

/** 输出流 */
@property (nonatomic, strong) NSOutputStream *outputSream;

/**  */
@property (nonatomic, assign) CFRunLoopRef currentRunLoop;

/** 进度 */
@property (nonatomic, copy) void(^progressBlock)(float);

/** 返回的路径 */
@property (nonatomic, copy) void(^completionBlock)(NSString *);

/** 错误信息 */
@property (nonatomic, copy) void(^faiedBlock)(NSString *);


/** connection */
@property (nonatomic, strong) NSURLConnection *conntection;

@end

@implementation DownLoader

- (void)downloadWithUrl:(NSURL *)url {
    // 1、检查服务器上的大小
    [self serverFileInfoWithUrl:url];
//     NSLog(@"filePath=%@ length=%lld",self.filePath, self.expectedContentLength);
    // 2、检查本地文件大小
    if (![self checkLocalFileInfo]) {
        NSLog(@"下载完毕");
    }
    // 3、如果需要从服务器开始下载
    [self downloadFile];
}

- (void)downloadWithUrl:(NSURL *)url progress:(void (^)(float))progress completion:(void (^)(NSString *))completion faied:(void (^)(NSString *))faied {
    // 保存属性
    self.downloadUrl = url;
    self.progressBlock = progress;
    self.completionBlock = completion;
    self.faiedBlock = faied;
    
    // 1、检查服务器上的大小
    [self serverFileInfoWithUrl:url];
    
//     NSLog(@"filePath=%@ length=%lld",self.filePath, self.expectedContentLength);
    // 2、检查本地文件大小
    if (![self checkLocalFileInfo]) {
        NSLog(@"下载完毕");
        // 主线程回调
        if (self.completionBlock) {
            dispatch_async(dispatch_get_main_queue(), ^{
                self.completionBlock(self.filePath);
            });
        }
        
        return;
    }
    // 3、如果需要从服务器开始下载
    [self downloadFile];
}

// 暂停
- (void)pause {
    NSLog(@"暂停");
    [self.conntection cancel];
}
   

// 从服务器开始下载 (从self.currentSize开始下载文件)
- (void)downloadFile {
    
    // 子线程下载
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        // 建立请求
        NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:self.downloadUrl cachePolicy:1 timeoutInterval:keyTimeOut];
        // 设置下载的字节范围 从 self.currentSize 开始之后的所有字节
        NSString *rangeStr = [NSString stringWithFormat:@"bytes=%lld-",self.currentSize];
        // 设置请求头字段
        [request setValue:rangeStr forHTTPHeaderField:@"Range"];
    //    [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse * _Nullable response, NSData * _Nullable data, NSError * _Nullable connectionError) {
    //        NSLog(@"下载完毕");
    //    }];
        // 开始网络连接
        self.conntection = [NSURLConnection connectionWithRequest:request delegate:self];
        [self.conntection start];
        
        self.currentRunLoop = CFRunLoopGetCurrent();
        CFRunLoopRun();
    });

}

#pragma mark - NSURLConnection Delegate

// 接收到服务器的响应
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
    // 打开输出流
    self.outputSream = [[NSOutputStream alloc] initToFileAtPath:self.filePath append:YES];
    [self.outputSream open];
}

- (void)connection:(NSURLConnection *)connection   didSendBodyData:(NSInteger)bytesWritten
        totalBytesWritten:(NSInteger)totalBytesWritten
totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite {
    
}

// 接收到数据 用输出流进行拼接
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
    // 追加数据
    [self.outputSream write:data.bytes maxLength:data.length];
    // 记录文件的长度
    self.currentSize += data.length;
    float progress = (float) self.currentSize / self.expectedContentLength;
//    NSLog(@"%f %@",progress,[NSThread currentThread]);
    if (self.progressBlock) {
        self.progressBlock(progress);
    }

}

// 所有下载完毕
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
    // 关闭
    [self.outputSream close];
    CFRunLoopStop(self.currentRunLoop);
    // 主线程回调
    if (self.completionBlock) {
        dispatch_async(dispatch_get_main_queue(), ^{
            self.completionBlock(self.filePath);
        });
    }

}

// 出错
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
    // 关闭
    [self.outputSream close];
    
    CFRunLoopStop(self.currentRunLoop);
    
    if (self.faiedBlock) {
        self.faiedBlock(error.localizedDescription);
    }
}

// 检查本地文件大小
- (BOOL)checkLocalFileInfo {
    BOOL isExist = [[NSFileManager defaultManager] fileExistsAtPath:self.filePath];
    long long fileSize = 0;
    // 文件是否存在
    if (isExist) {
        // 获取文件大小
        NSDictionary *attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:self.filePath error:NULL];
//        NSLog(@"%@",attributes);
//        fileSize = [attributes[NSFileSize] longLongValue];
        // 利用分类方法获取文件大小
        fileSize = [attributes fileSize];
    }
    /**
     如果size小于服务器的大小，从本地文件的长度开始下载（续传）
     如果size等于服务器的大小，认为文件已经下载完毕
     如果size大于服务器的大小，直接删掉，重新下载（断点续传出错）
     */
    if (fileSize > self.expectedContentLength) {
        // 删除文件
        [[NSFileManager defaultManager] removeItemAtPath:self.filePath error:NULL];
        fileSize = 0;
    }
    self.currentSize = fileSize;
    if (fileSize == self.expectedContentLength) {
        return NO;
    }
    return YES;
}
 
// 检查服务器上的大小
- (void)serverFileInfoWithUrl:(NSURL *)url {
    
    self.downloadUrl = url;
    // 请求
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:1 timeoutInterval:keyTimeOut];
    
    request.HTTPMethod = @"HEAD";
    // 建立网络连接
    NSURLResponse *response = nil;
    NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:NULL];
//    NSLog(@"data=%@ response=%@ length=%lld",data, response, response.expectedContentLength);
    // 记录服务器的文件信息
    // 文件长度
    self.expectedContentLength = response.expectedContentLength;
    // 建议保存的文件名 （将下载的文件保存在tem中，系统会自动回收）
    self.filePath = [NSTemporaryDirectory() stringByAppendingPathComponent:response.suggestedFilename];
}

@end
